/*
 * xlogger.cpp
 *
 *  Created on: 2009-7-11
 *      Author: fanliangliang
 */

#include "xlogger.h"
#include <bautils.h>
#include <charconv.h>
#include <utf.h>
#include <e32cons.h>

#if PLATFORM_VERSION > 20
#include <e32debug.h> // for RDebug
#else
#include <e32svr.h>   // for RDebug
#endif

#define UNREFERENCED_PARAMETER(p) (p)

_LIT(KStringTrue, "true");
_LIT(KStringFalse, "false");
_LIT(KStringNull, "null");
_LIT(KStringCRLF, "\r\n");
_LIT(KFormatDes, "%S");
_LIT(KFormatChar, "%c");
_LIT(KFormatQuotedChar, "'%c'");
_LIT(KFormatInt, "%d");
_LIT(KFormatUint, "%u");
_LIT(KFormatPointer, "0x%08x");
_LIT(KFormatReal, "%f");
_LIT(KFormatPoint, "TPoint(%d, %d)");
_LIT(KFormatSize, "TSize(%d, %d)");
_LIT(KFormatRect, "TRect(%d, %d, %d, %d)");
_LIT(KFormatPanic, "Panic %S %d");
_LIT(KFormatLeave, "Leave %d");
_LIT(KFormatTime, "%F%Y-%M-%D %H:%T:%S"); // 2009-04-29 05:45:27
_LIT(KFormatLog, "[%S][%s][%S, %d]%S\r\n"); // [2009-04-29 05:45:27][ERR][source.cpp, 44]this is a log message


CLogBuffer* CLogBuffer::NewL() {
	CLogBuffer* self = NewLC();
	CleanupStack::Pop(self);
	return self;
}

CLogBuffer* CLogBuffer::NewLC() {
	CLogBuffer* self = new (ELeave) CLogBuffer();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

CLogBuffer::CLogBuffer()
:iBuffer(NULL)
,iIsOverflowed(false) {

}

CLogBuffer::~CLogBuffer() {
	delete iBuffer;
}

void CLogBuffer::ConstructL() {
	iBuffer = HBufC::NewL(KInitialBufferSize);
}

void CLogBuffer::AppendFormat(TRefByValue<const TDesC> aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
	AppendFormatList(aFormat, aList);
	VA_END(aList);
}

void CLogBuffer::AppendFormatList(const TDesC& aFormat, VA_LIST aList) {
	TBool outOfMemory = EFalse;
	do {
		iIsOverflowed = false;
		TPtr ptr(iBuffer->Des());
		TInt length = ptr.Length();
		ptr.AppendFormatList(aFormat, aList, this);
		if (iIsOverflowed) {
			ptr.SetLength(length);
			HBufC* newBuffer = iBuffer->ReAlloc(ptr.MaxLength() + KIncrementalBufferSize);
			if (newBuffer != NULL) {
				iBuffer = newBuffer;
			}
			else {
				outOfMemory = ETrue;
			}
		}
	}
	while (iIsOverflowed && !outOfMemory);
}

void CLogBuffer::Format(TRefByValue<const TDesC> aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
	FormatList(aFormat, aList);
	VA_END(aList);
}

void CLogBuffer::FormatList(const TDesC& aFormat, VA_LIST aList) {
	Zero();
	AppendFormatList(aFormat, aList);
}

void CLogBuffer::Append(bool aValue) {
	AppendFormat(KFormatDes(), aValue ? &KStringTrue : &KStringFalse);
}

void CLogBuffer::Append(char aValue) {
	AppendFormat(KFormatChar(), (TUint)aValue);
}

void CLogBuffer::Append(signed char aValue) {
	AppendFormat(KFormatInt(), aValue);
}

void CLogBuffer::Append(unsigned char aValue) {
	AppendFormat(KFormatUint(), aValue);
}

void CLogBuffer::Append(short aValue) {
	AppendFormat(KFormatInt(), aValue);
}

void CLogBuffer::Append(unsigned short aValue) {
	AppendFormat(KFormatUint(), aValue);
}

void CLogBuffer::Append(int aValue) {
	AppendFormat(KFormatInt(), aValue);
}

void CLogBuffer::Append(unsigned int aValue) {
	AppendFormat(KFormatUint(), aValue);
}

void CLogBuffer::Append(long aValue) {
	AppendFormat(KFormatInt(), aValue);
}

void CLogBuffer::Append(unsigned long aValue) {
	AppendFormat(KFormatUint(), aValue);
}

void CLogBuffer::Append(float aValue) {
	AppendFormat(KFormatReal(), aValue);
}

void CLogBuffer::Append(double aValue) {
	AppendFormat(KFormatReal(), aValue);
}

void CLogBuffer::Append(long double aValue) {
	AppendFormat(KFormatReal(), aValue);
}

void CLogBuffer::Append(const void* aValue) {
	AppendFormat(KFormatPointer(), aValue);
}

void CLogBuffer::Append(const char* aValue) {
	aValue == NULL ? Append(KStringNull()) : Append(TPtrC8((const TUint8*)aValue));
}

void CLogBuffer::Append(TChar aValue) {
	AppendFormat(KFormatChar(), (TUint)aValue);
}

void CLogBuffer::Append(TInt64 aValue) {
	TBuf<32> buffer;
	buffer.Num(aValue);
	Append(buffer);
}

void CLogBuffer::Append(const TDesC16& aDes) {
	AppendFormat(KFormatDes(), &aDes);
}

void CLogBuffer::Append(const TDesC16* aValue) {
	aValue == NULL ? Append(KStringNull()) : Append(*aValue);
}

void CLogBuffer::Append(const TDesC8& aDes) {
	Append(&aDes);
}

void CLogBuffer::Append(const TDesC8* aValue) {
	if (aValue == NULL) {
		Append(KStringNull());
	}
	else {
		HBufC16* unicodeValue = CLogger::Instance()->ConvertToUnicode(*aValue);
		Append(unicodeValue);
		delete unicodeValue;
	}
}

TPtrC8 CLogBuffer::PtrC8() const {
	return TPtrC8((const TUint8*)iBuffer->Ptr(), iBuffer->Size());
}

const TDesC& CLogBuffer::DesC() const {
	return *iBuffer;
}

TInt CLogBuffer::Length() const {
	return iBuffer->Des().Length();
}

void CLogBuffer::Zero() {
	iBuffer->Des().Zero();
}

void CLogBuffer::Overflow(TDes& /*aDes*/) {
	iIsOverflowed = true;
}



CLogRecord* CLogRecord::NewL() {
	CLogRecord* self = NewLC();
	CleanupStack::Pop(self);
	return self;
}

CLogRecord* CLogRecord::NewLC() {
	CLogRecord* self = new (ELeave) CLogRecord();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

CLogRecord::CLogRecord()
:iLevel(ELevelTrace)
,iMessage(NULL)
,iLine(0) {
	
}

CLogRecord::~CLogRecord() {
	delete iMessage;
}

void CLogRecord::ConstructL() {
	iMessage = CLogBuffer::NewL();
}


static const TText* LogLevelToString(TLogLevel aLevel) {
	static const TText* const levelStrings[] = {
		_S("TRC"), // trace
		_S("DBG"), // debug
		_S("INF"), // info
		_S("WRN"), // warn
		_S("ERR"), // error
		_S("FTL")  // fatal
	};
	return levelStrings[aLevel];
}


CLogAppenderBase::CLogAppenderBase()
:iBuffer(NULL) {
	
}

CLogAppenderBase::~CLogAppenderBase() {
	delete iBuffer;
}

void CLogAppenderBase::DoAppendL(const CLogRecord& e) {
	TBuf<19> time;
	e.iTime.FormatL(time, KFormatTime());
	const TText* level = LogLevelToString(e.iLevel);
	iBuffer->Format(KFormatLog(), &time, level, &e.iFilename, e.iLine, &e.iMessage->DesC());
	DoAppendL(*iBuffer);
}

void CLogAppenderBase::BaseConstructL() {
	iBuffer = CLogBuffer::NewL();
}


CFileAppender* CFileAppender::NewL(const TDesC& aFilename, TUint aFileMode /*= EFileWrite | EFileStreamText*/) {
	CFileAppender* self = CFileAppender::NewLC(aFilename, aFileMode);
	CleanupStack::Pop(self);
	return self;
}

CFileAppender* CFileAppender::NewLC(const TDesC& aFilename, TUint aFileMode /*= EFileWrite | EFileStreamText*/) {
	CFileAppender* self = new (ELeave) CFileAppender();
	CleanupStack::PushL(self);
	self->ConstructL(aFilename, aFileMode);
	return self;
}

CFileAppender::CFileAppender() {

}

CFileAppender::~CFileAppender() {
	iFile.Close();
	iFs.Close();
}

void CFileAppender::ConstructL(const TDesC& aFilename, TUint aFileMode) {
	BaseConstructL();
	User::LeaveIfError(iFs.Connect());
	BaflUtils::EnsurePathExistsL(iFs, aFilename);
	User::LeaveIfError(iFile.Replace(iFs, aFilename, aFileMode));
}

void CFileAppender::DoAppendL(const CLogBuffer& iBuffer) {
	HBufC8* utf8Buffer = CnvUtfConverter::ConvertFromUnicodeToUtf8L(iBuffer.DesC());
	iFile.Write(*utf8Buffer);
	iFile.Flush();
	delete utf8Buffer;
}



CDebugAppender* CDebugAppender::NewL() {
	CDebugAppender* self = NewLC();
	CleanupStack::Pop(self);
	return self;
}

CDebugAppender* CDebugAppender::NewLC() {
	CDebugAppender* self = new (ELeave) CDebugAppender();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

CDebugAppender::CDebugAppender() {
	
}

CDebugAppender::~CDebugAppender() {

}

void CDebugAppender::ConstructL() {
	BaseConstructL();
}

void CDebugAppender::DoAppendL(const CLogBuffer& iBuffer) {
	RDebug::Print(KFormatDes(), &iBuffer.DesC());
}



CConsoleAppender* CConsoleAppender::NewL(const TDesC& aTitle) {
	return NewL(aTitle, TSize(KConsFullScreen, KConsFullScreen));
}

CConsoleAppender* CConsoleAppender::NewL(const TDesC& aTitle, TSize aSize) {
	CConsoleAppender* self = NewLC(aTitle, aSize);
	CleanupStack::Pop(self);
	return self;
}

CConsoleAppender* CConsoleAppender::NewLC(const TDesC& aTitle) {
	return NewLC(aTitle, TSize(KConsFullScreen, KConsFullScreen));
}

CConsoleAppender* CConsoleAppender::NewLC(const TDesC& aTitle, TSize aSize) {
	CConsoleAppender* self = new (ELeave) CConsoleAppender();
	CleanupStack::PushL(self);
	self->ConstructL(aTitle, aSize);
	return self;
}

CConsoleAppender::CConsoleAppender()
:iConsole(NULL) {
	
}

CConsoleAppender::~CConsoleAppender() {
	delete iConsole;
}

void CConsoleAppender::ConstructL(const TDesC& aTitle) {
	ConstructL(aTitle, TSize(KConsFullScreen, KConsFullScreen));
}

void CConsoleAppender::ConstructL(const TDesC& aTitle, TSize aSize) {
	BaseConstructL();
	iConsole = Console::NewL(aTitle, aSize);
}

void CConsoleAppender::DoAppendL(const CLogBuffer& iBuffer) {
	iConsole->Printf(KFormatDes(), &iBuffer.DesC());
}



CAsserter* CAsserter::NewL(CLogger& aLogger) {
	CAsserter* self = NewLC(aLogger);
	CleanupStack::Pop(self);
	return self;
}

CAsserter* CAsserter::NewLC(CLogger& aLogger) {
	CAsserter* self = new (ELeave) CAsserter(aLogger);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

#undef XASSERT_A
#undef XASSERT_B
CAsserter::CAsserter(CLogger& aLogger)
:XASSERT_A(*this)
,XASSERT_B(*this)
,iLogger(aLogger)
,iBuffer(NULL) {

}

CAsserter::~CAsserter() {
	delete iBuffer;
}

void CAsserter::ConstructL() {
	iBuffer = CLogBuffer::NewL();
}

CAsserter& CAsserter::LogExpression(const char* aExpression, const TSentry& /*aSentry*/) {
	HBufC16* unicodeExpression = iLogger.ConvertToUnicode(aExpression);
	iBuffer->AppendFormat(_L("    Expression: %S\r\n"), unicodeExpression);
	delete unicodeExpression;
	return *this;
}

template <typename T>
CAsserter& AddParameterHelper(CAsserter& aAsserter, const char* aName, const TDesC& aFormat, const T& aValue) {
	TBuf<256> buffer;
	buffer.AppendFormat(aFormat, aValue);
	return aAsserter.AddOtherParameter(aName, buffer);
}

CAsserter& CAsserter::AddParameter(const char* aName, bool aValue) {
	return AddOtherParameter(aName, aValue ? KStringTrue() : KStringFalse());
}

CAsserter& CAsserter::AddParameter(const char* aName, char aValue) {
	return AddParameterHelper(*this, aName, KFormatQuotedChar(), TInt(aValue));
}

CAsserter& CAsserter::AddParameter(const char* aName, TChar aValue) {
	return AddParameterHelper(*this, aName, KFormatQuotedChar(), (TUint)aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, TInt32 aValue) {
	return AddParameterHelper(*this, aName, KFormatInt(), aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, TUint32 aValue) {
	return AddParameterHelper(*this, aName, KFormatUint(), aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, TInt64 aValue) {
	TBuf<32> buffer;
	buffer.Num(aValue);
	return AddParameter(aName, buffer);
}

CAsserter& CAsserter::AddParameter(const char* aName, TReal32 aValue) {
	return AddParameterHelper(*this, aName, KFormatReal(), aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, TReal64 aValue) {
	return AddParameterHelper(*this, aName, KFormatReal(), aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, const void* aValue) {
	return AddParameterHelper(*this, aName, KFormatPointer(), aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, const char* aValue) {
	return aValue == NULL ? AddOtherParameter(aName, KStringNull()) : AddParameter(aName, TPtrC8((const TUint8*)aValue));
}

CAsserter& CAsserter::AddParameter(const char* aName, const TDesC16& aValue) {
	return AddDescriptorParameter(aName, aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TDesC16* aValue) {
	return aValue == NULL ? AddOtherParameter(aName, KStringNull()) : AddParameter(aName, *aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TDesC8& aValue) {
	return AddParameter(aName, &aValue);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TDesC8* aValue) {
	if (aValue == NULL) {
		return AddParameter(aName, static_cast<const TDesC16*>(NULL));
	}
	else {
		HBufC16* unicodeValue = iLogger.ConvertToUnicode(*aValue);
		AddParameter(aName, unicodeValue);
		delete unicodeValue;
		return *this;
	}
}

CAsserter& CAsserter::AddParameter(const char* aName, const TPoint& aValue) {
	TBuf<32> buffer;
	buffer.Format(KFormatPoint(), aValue.iX, aValue.iY);
	return AddOtherParameter(aName, buffer);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TSize& aValue) {
	TBuf<32> buffer;
	buffer.Format(KFormatSize(), aValue.iWidth, aValue.iHeight);
	return AddOtherParameter(aName, buffer);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TRect& aValue) {
	TBuf<32> buffer;
	buffer.Format(KFormatRect(), aValue.iTl.iX, aValue.iTl.iY, aValue.Width(), aValue.Height());
	return AddOtherParameter(aName, buffer);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TTime& aValue) {
	TBuf<19> buffer;
	aValue.FormatL(buffer, KFormatTime());
	return AddOtherParameter(aName, buffer);
}

CAsserter& CAsserter::AddParameter(const char* aName, const TDateTime& aValue) {
	TTime time(aValue);
	return AddParameter(aName, time);
}

CAsserter& CAsserter::AddDescriptorParameter(const char* aName, const TDesC16& aValue) {
	return AddParameterImplement(aName, _L("    %S: \"%S\"\r\n"), aValue);
}

CAsserter& CAsserter::AddOtherParameter(const char* aName, const TDesC16& aValue) {
	return AddParameterImplement(aName, _L("    %S: %S\r\n"), aValue);
}

CAsserter& CAsserter::AddParameterImplement(const char* aName, const TDesC16& aFormat, const TDesC& aValue) {
	HBufC16* unicodeName = iLogger.ConvertToUnicode(aName);
	iBuffer->AppendFormat(aFormat, unicodeName, &aValue);
	delete unicodeName;
	return *this;
}

void CAsserter::Commit() {
	Commit(KNullDesC());
}

void CAsserter::Commit(const TDesC& aMessage) {
	if (iBuffer->Length() > 0) {
		iLogger.Log("\r\n\r\n    Assertion failed: %S\r\n%S", &aMessage, &iBuffer->DesC());
		iBuffer->Zero();
	}
}

void CAsserter::xpanic(const TDesC& aCategory, TInt aReason) {
	TBuf<64> message;
	message.Format(KFormatPanic(), &aCategory, aReason);
	Commit(message);
	User::Panic(aCategory, aReason);
}

void CAsserter::xleave(TInt aReason) {
	TBuf<64> message;
	message.Format(KFormatLeave(), aReason);
	Commit(message);
	User::Leave(aReason);
}

void CAsserter::xmessage(const TDesC& aMessage) {
	Commit(aMessage);
}

void CAsserter::xmessage(const char* aMessage) {
	HBufC16* unicodeMessage = iLogger.ConvertToUnicode(aMessage);
	Commit(*unicodeMessage);
	delete unicodeMessage;
}


CHexDumper* CHexDumper::NewL(CLogger& aLogger) {
	CHexDumper* self = NewLC(aLogger);
	CleanupStack::Pop(self);
	return self;
}

CHexDumper* CHexDumper::NewLC(CLogger& aLogger) {
	CHexDumper* self = new (ELeave) CHexDumper(aLogger);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

CHexDumper::CHexDumper(CLogger& aLogger)
:iLogger(aLogger)
,iBuffer(NULL) {
	
}

CHexDumper::~CHexDumper() {
	delete iBuffer;
}

void CHexDumper::ConstructL() {
	iBuffer = CLogBuffer::NewL();
}

void CHexDumper::operator()(const TDesC8& aData) {
	(*this)(aData.Ptr(), aData.Size());
}

void CHexDumper::operator()(const TDesC16& aData) {
	(*this)(aData.Ptr(), aData.Size());
}

void CHexDumper::operator()(const TAny* aData, TInt aSize) {
	iBuffer->Zero();
	DumpData(aData, aSize);
	iLogger.Log("\r\n%S", &iBuffer->DesC());
}

void CHexDumper::operator()(const TDesC8& aData, const char* aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
	Dump(aData.Ptr(), aData.Size(), aFormat, aList);
	VA_END(aList);
}

void CHexDumper::operator()(const TDesC16& aData, const char* aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
	Dump(aData.Ptr(), aData.Size(), aFormat, aList);
	VA_END(aList);
}

void CHexDumper::operator()(const TAny* aData, TInt aSize, const char* aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
	Dump(aData, aSize, aFormat, aList);
	VA_END(aList);
}

void CHexDumper::DumpHeader() {
	iHeaderBuffer.Zero();
	iHeaderBuffer.AppendFill(' ', 10);
	for (TInt i = 0; i < 16; ++i) {
		iHeaderBuffer.AppendFormat(_L(" %x "), i);
	}
	iHeaderBuffer.Append(KStringCRLF());
	iBuffer->Append(iHeaderBuffer);
}

void CHexDumper::DumpLine(TInt aLine, const TInt8* aData, TInt aSize) {
	if (aLine >= 0 && aData != NULL && aSize > 0) {
		iLineBuffer.Zero();
		iLineBuffer.AppendFormat(_L("%08x: "), aLine * 16);

		TBuf8<16> str;
		for (TInt i = 0; i < aSize; ++i) {
			TUint8 ch = aData[i];
			str.Append(ch > 31 ? ch : '.');
			iLineBuffer.AppendFormat(_L("%02X "), ch);
		}

		iLineBuffer.AppendFill(' ', 3 * (16 - aSize));

		HBufC16* unicodeLine = iLogger.ConvertToUnicode(str);
		iLineBuffer.AppendFormat(_L("| %S\r\n"), unicodeLine);
		delete unicodeLine;
		
		iBuffer->Append(iLineBuffer);
	}
}

void CHexDumper::DumpData(const TAny* aData, TInt aSize) {
	if (aData != NULL && aSize > 0) {
		iBuffer->Append(KStringCRLF());
		DumpHeader();
		
		const TInt8* begin = static_cast<const TInt8*>(aData);
		TInt line = aSize / 16;
		for (TInt i = 0; i < line; ++i) {
			DumpLine(i, begin, 16);
			begin += 16;
		}

		DumpLine(line, begin, aSize % 16);
	}
}

void CHexDumper::Dump(const TAny* aData, TInt aSize, const char* aFormat, VA_LIST aList) {
	iBuffer->Zero();
	HBufC16* unicodeLine = iLogger.ConvertToUnicode(aFormat);
	iBuffer->AppendFormatList(*unicodeLine, aList);
	iBuffer->Append(KStringCRLF());
	delete unicodeLine;

	DumpData(aData, aSize);
	iLogger.Log("%S", &iBuffer->DesC());
}



CLogger* CLogger::NewL() {
	CLogger* self = CLogger::NewLC();
	CleanupStack::Pop(self);
	return self;
}

CLogger* CLogger::NewLC() {
	CLogger* self = new (ELeave) CLogger();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

CLogger::CLogger()
:iLevel(ELevelTrace)
,iRecord(NULL)
,iAsserter(NULL)
,iHexDumper(NULL)
,iConverter(NULL)
,iInitializeCount(0)
,iCoutStyle(false) {
	
}

CLogger::~CLogger() {
	iAppenders.ResetAndDestroy();
	iFs.Close();
	delete iConverter;
	delete iRecord;
	delete iAsserter;
	delete iHexDumper;
}

void CLogger::ConstructL() {
	iRecord = CLogRecord::NewL();
	iAsserter = CAsserter::NewL(*this);
	iHexDumper = CHexDumper::NewL(*this);
	
	iConverter = CCnvCharacterSetConverter::NewL();
	iFs.Connect();
	
	if (iConverter->PrepareToConvertToOrFromL(KCharacterSetIdentifierGbk, iFs)
			!= CCnvCharacterSetConverter::EAvailable) {
		User::Leave(KErrNotSupported);
	}
	
	iState = CCnvCharacterSetConverter::KStateDefault;
}

static const TInt KLoggerHandle = 0xABCDEFFF;

CLogger* CLogger::Instance() {
	CLogger* instance = static_cast<CLogger*>(UserSvr::DllTls(KLoggerHandle));
	if (instance == NULL) {
		instance = CLogger::NewL();
		UserSvr::DllSetTls(KLoggerHandle, instance);
	}
	return instance;
}

CLogger* CLogger::InstanceFor(TLogLevel aLevel) {
	CLogger* instance = Instance();
	if (instance->IsEnabledFor(aLevel)) {
		return instance;
	}
	return NULL;
}

bool CLogger::IsInitialized() {
	return UserSvr::DllTls(KLoggerHandle) != NULL;
}

void CLogger::InitializeOnceImplement(TLoggerInitializer aInitializer) {
	CLogger* instance = Instance();
	++(instance->iInitializeCount);
	if (instance->iInitializeCount == 1) {
		if (aInitializer == NULL) {
			aInitializer = DefaultInitializer;
		}
		aInitializer(instance);
	}
}

void CLogger::DestroyOnceImplement() {
	if (IsInitialized()) {
		CLogger* instance = Instance();
		--(instance->iInitializeCount);
		if (instance->iInitializeCount == 0) {
			Destroy();
		}
	}
}

void CLogger::Destroy() {
	delete static_cast<CLogger*>(UserSvr::DllTls(KLoggerHandle));
	UserSvr::DllSetTls(KLoggerHandle, NULL);
	UserSvr::DllFreeTls(KLoggerHandle);
}

void CLogger::DefaultInitializer(CLogger* aLogger) {
	RProcess currentProcess;
	TFileName filename;
	
#ifdef EKA2
	filename.Copy(currentProcess.FileName());
#else
	Dll::FileName(filename);
#endif
	
	filename.LowerCase();
	
	TPtrC name;
	TRAPD(error, name.Set(TParsePtrC(filename).Name()))
	
	TBuf<128> logfile;
	
	if (error == KErrNone) {
		logfile.Format(_L("c:\\data\\%S.txt"), &name);
	}
	else {
		TFullName fullname(currentProcess.FullName());
		fullname.LowerCase();
		TInt pos = fullname.Locate('[');
		if (pos != KErrNotFound) {
			name.Set(fullname.Left(pos));
		}
		else {
			name.Set(fullname);
		}
		logfile.Format(_L("c:\\data\\%S.txt"), &name);
	}
	
	aLogger->AddAppender(CFileAppender::NewL(logfile));
}

CLogger& CLogger::SetLogParameters(TLogLevel aLevel, const TDesC& aFile, TInt aLine, const TSentry& /*aSentry*/) {
	return SetLogParameters(aLevel, aFile, aLine);
}

CLogger& CLogger::SetLogParameters(TLogLevel aLevel, const TDesC& aFile, TInt aLine) {
	iCoutStyle = true;
	
	iRecord->iLevel = aLevel;
	iRecord->iLine = aLine;
	
	TInt pos = aFile.LocateReverse(TChar('\\'));
	if (pos == KErrNotFound) {
		pos = aFile.LocateReverse(TChar('/'));
	}
	if (pos != KErrNotFound) {
		iRecord->iFilename = aFile.Mid(pos + 1);
	}
	else {
		iRecord->iFilename = aFile;
	}
	
	return *this;
}

void CLogger::Log(const char* aFormat, VA_LIST aList) {
	iCoutStyle = false;
	HBufC16* format = ConvertToUnicode(aFormat);
	if (format != NULL) {
		iRecord->iMessage->FormatList(*format, aList);
		delete format;
		DoAppend();
	}
}

void CLogger::Log(const char* aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
  	Log(aFormat, aList);
	VA_END(aList);
}

void CLogger::operator()(const char* aFormat, ...) {
	VA_LIST aList;
	VA_START(aList, aFormat);
  	Log(aFormat, aList);
	VA_END(aList);
}

HBufC16* CLogger::ConvertToUnicode(const TDesC8& aForeign) {
	HBufC16* unicodeText = HBufC16::New(aForeign.Length());
	if (unicodeText != NULL) {
		TPtr16 ptr = unicodeText->Des();
		if (iConverter->ConvertToUnicode(ptr, aForeign, iState)
				== CCnvCharacterSetConverter::EErrorIllFormedInput) {
			return NULL;
		}
	}
	return unicodeText;
}

HBufC16* CLogger::ConvertToUnicode(const char* aForeign) {
	return ConvertToUnicode(TPtrC8((const TUint8*)(aForeign)));
}

void CLogger::AddAppender(MLogAppender* aAppender) {
	iAppenders.Append(aAppender);
}

bool CLogger::IsEnabledFor(TLogLevel aLevel) const {
	return aLevel >= iLevel;
}

void CLogger::SetLevel(TLogLevel aLevel) {
	iLevel = aLevel;
}

TLogLevel CLogger::Level() const {
	return iLevel;
}

CAsserter& CLogger::Asserter() {
	return *iAsserter;
}

CHexDumper& CLogger::HexDumper() {
	return *iHexDumper;
}

void CLogger::Commit() {
	if (iCoutStyle) {
		DoAppend();
	}
}

void CLogger::DoAppend() {
	TRAPD(error, DoAppendL());
	UNREFERENCED_PARAMETER(error);
}

void CLogger::DoAppendL() {
	iRecord->iTime.HomeTime();
	TInt count = iAppenders.Count();
	for (TInt i = 0; i < count; ++i) {
		if (MLogAppender* appender = iAppenders[i]) {
			appender->DoAppendL(*iRecord);
		}
	}
	iRecord->iMessage->Zero();
}



TScopeTracer::TScopeTracer(const char* aScopeName, const TDesC& aFile, TInt aLine)
:iLogger(NULL)
,iScopeName(NULL)
,iFile(NULL)
,iLine(0)
,iIsLeaved(true)
,iIsCleanedup(false) {
	iLogger = CLogger::InstanceFor(ELevelTrace);
	if (iLogger != NULL) {
		iScopeName = iLogger->ConvertToUnicode(aScopeName);
		iFile = aFile.Alloc();
		iLine = aLine;
		if (iScopeName != NULL && iFile != NULL) {
			CleanupStack::PushL(TCleanupItem(TScopeTracer::OnCleanup, this));
			iLogger->SetLogParameters(ELevelTrace, *iFile, iLine);
			iLogger->Log("Enter %S", iScopeName);
		}
	}
}

TScopeTracer::~TScopeTracer() {
	if (iLogger != NULL && iScopeName != NULL && iFile != NULL) {
		if (!iIsCleanedup) {
			iIsLeaved = false;
			CleanupStack::PopAndDestroy;;
		}
	}
}

void TScopeTracer::Cleanup() {
	if (!iIsCleanedup) {
		iIsCleanedup = true;
		iLogger->SetLogParameters(ELevelTrace, *iFile, iLine);
		if (iIsLeaved) {
			iLogger->Log("Leave %S", iScopeName);
		}
		else {
			iLogger->Log("Exit  %S", iScopeName);
		}
		delete iScopeName;
		delete iFile;
	}
}

void TScopeTracer::OnCleanup(TAny* aObject) {
	if (TScopeTracer* tracer = static_cast<TScopeTracer*>(aObject)) {
		tracer->Cleanup();
	}
}
